\environment doc-style
\starttext
\title{The Snail Module}
\blank[line]
\midaligned{Li Yanrui (lyr.m2@live.cn)}
\useURL[snail][https://github.com/liyanrui/snail]
\blank
\midaligned{\from[snail]}
\blank[line]
\setupbackgrounds[page][background=timestamp]
\startcolumns[n=2]
\placecontent
\stopcolumns

\subject{Introduction}

Maybe everyone knows MetaPost is great at drawing accurate science diagrams. But that's not the whole story! Think of it this way, it's actually a programming language for drawing vector graphics with simple syntax. You can write code to create the same great things as in those industry software (e.g., Adobe Illustrator, CorelDraw or Inkscape).

Don't believe the above claim I made. Let's be real. I've never worked as a graphic designer. Visual tools are much faster and easier for design work than coding with MetaPost. Exactly like how most pepole prefer movies over books these days for storytelling.

However, in this technological era, I wrote a small MetaPost module for drawing flowchart and diagrammatic illustrations within \ConTeXt\ environment. Is this somehow a lamentable act? Certainly not. I have never intended to resist technological progress. Our era pursues its trajectory; I pursue mine. The primary driver for writing this module was the absence of satisfactory flowchart software on my Linux desktop.

This module is named \boxquote{snail}. True to its name, it's slow at drawing diagrams.

Its development has been even slower. Around 2018, while properly learning MetaPost for the first time, I wrote the version 1 as practice work. It did not work well. In fact, after finishing it, I showed it off to a few friends then never used it again. In 2023, I relearned MetaPost and created the version 2. This one actually worked---I'd say it worked well. But I discovered MetaPost supports Chinese variable and macro names, so I built this version as a Chinese diagramming language. Both the variables and macros got quirky Chinese names. That's why I've never showed it to anyone since finishing it.

\useURL[ctxnotes][https://github.com/liyanrui/ConTeXt-notes]

Seven years gone in a blink and it's already 2025. Honestly I've achieved nothing remarkable. Feeling low, I revisited MetaPost and wrote some documentation. Along the way, third version of snail took shape. Maybe this is all I'm capable of, simple tools for simple needs.

\section{First Snail Diagram}

This is the smallest snail drawing environment:

\startTEX
\usemodule[snail]
\startMPpage
% put metapost code here!
\stopMPpage
\stopTEX

To work with the snail module, you need \ConTeXt\ LMTX. All that's left is to write some MetaPost code inside the \type{MPpage} environment. To compile a \TEX\ source file \boxquote{foo.tex} into \boxquote{foo.pdf}, in your terminal or comand window, you can excute the command 

\starttyping
context foo.tex
\stoptyping

or

\starttyping
context foo
\stoptyping

Then you can get the \boxquote{foo.pdf} file in the same directory. The above process can be expressed as the following snail code.

\startexample
\usemodule[snail]
\startMPpage
snailfam_t a;
slug(a1, "foo.tex");
snail(a2, "{\darkred\type{context}}") at (3cm, 0);
slug(a3, "foo.pdf") at (3cm, -2cm);
showsnails a1, a2, a3;
showflows a1 => a2, a2 => a3;
\stopMPpage
\stopexample
\example[option=TEX][first-diagram]{Your first diagram}{\externalfigure[figures/01.pdf]}

\section{Nodes}

All snail objects, i.e. nodes, must be declared before defining them. There are two declaration methods. The first one declares a group of nodes with the macro \type{snail_t}. The other one declares a group node sequence (or array) with the macro \type{snailfam_t}.

For instance, to declare the nodes \type{foo} and \type{bar}, you can use

\startMP
snail_t foo, bar;
\stopMP

To create multiple sequence of nodes, use MetaPost's array syntax. The following code declares two node arrays \type{A} and \type{B}:

\startMP
snailfam_t A, B;
\stopMP

The elements of a node array are accessible with indices, e.g., \type{A[1]}, \type{A[2]}, \cdots\  or \type{A1}, \type{A2}, \cdots .

Snail provides three node types constructed via the macros \type{snail}, \type{slug} and \type{avatar}. \type{snail} creates nodes with visiable frames, while \type{slug} creates frameless nodes. For specialized requirements, \type{avatar} import external figures as nodes. By default, the centers of newly created nodes are at the origin \type{(0, 0)}. The \type{at} macro can set a node's center when creating it. The \type{showsnails} macro draws all nodes that it accept.

The following example creates three nodes of different types.

\startexample
snailfam_t a;
snail(a1, "I am $a_1$") at (4cm, 0);
slug(a2, "I am $a_2$") at (3cm, 1.5cm);
avatar(a3, "demo.png", 3cm, "auto");
showsnails a1, a2, a3;
\stopexample
\example[option=MP][kinds-of-nodes]{Three kinds of nodes}{\externalfigure[figures/02.pdf]}

The second argument of \type{avatar} is the image file or path name. The third and fourth arguments are the width and height of the figure respectively. You can set just one of them; put \type{"auto"} for the other and the snail module will calculate it from the figure’s aspect ratio.

\section{Anchors}

Every snail node has 25 anchor points. For the node \type{foo}, they are stored in \type{foo.anchors}, a two-dimensional array. The center of \type{foo} is \type{foo.anchors[0][0]} as shown below.

\startexample
snail_t foo;
snail(foo, "I am foo");
showsnails foo;

pickup pensquare scaled 8pt;
draw foo.anchors[0][0]
     withcolor transparent(1, .5, darkred);
\stopexample
\example[option=MP][center-anchor]{Center anchor}{\externalfigure[figures/03.pdf][width=5cm]}

If \type{foo.anchors[0][0]} is taken as the single anchor at level 0, then \type{foo.anchors[1][1]} through \type{foo.anchors[1][16]} are the 16 anchors at level 1. The following example lables the indices of these anchors at this level.

\startMP
for i = 1 upto 16:
  draw foo.anchors[1][i] withcolor darkblue;
  draw textext(decimal i) shifted foo.anchors[1][i] withcolor white;
endfor;
\stopMP
\placeExample[here][first-anchor-level]{First anchor level}{\externalfigure[figures/04.pdf][width=8cm]}

From \type{foo.anchors[2][1]} to \type{foo.anchors[2][8]} constitute the second level of the anchor points of the node \type{foo}. They lie outside the node's frame.

\startMP
for i = 1 upto 8:
  draw foo.anchors[2][i] withcolor darkgray;
  draw textext(decimal i) shifted foo.anchors[2][i] withcolor white;
endfor;
\stopMP
\placeExample[here][second-anchor-level]{Second anchor level}{\externalfigure[figures/05.pdf][width=8cm]}

To display all anchor points of a node, you can use the \type{snailenv.set} macro to set the snail module parameter \type{debug} to \type{true} before the \type{showsnails} macro statement.

\startexample
snail_t a;
snail(a, "Node a");
snailenv.set("debug", true);
showsnails a;
\stopexample
\example[option=MP][debug]{Node debug mode}{\externalfigure[figures/06.pdf]}

With help of anchor points, you can position a node by relative displacement with the snail syntax \boxquote{\type{put node at a from b}}. Example \in[put-at] aligns the anchor point \type{bar.anchors[2][4]} with the anchor point \type{foo.anchors[2][8]}.

\startexample
snail_t foo, bar;
snail(foo, "foo");
snail(bar, "bar");
put bar at foo.anchors[2][8]
        from bar.anchors[2][4];
snailenv.set("debug", true);
showsnails foo, bar;
\stopexample
\example[option=MP][put-at]{Node relative displacement}{\externalfigure[figures/07.pdf]}

If you feel hard to remember these indices of anchor points, you can name them by defining some MetaPost macro, for example:

\startexample
def _e_ = anchors[1][1] enddef; def _n_ = anchors[1][5] enddef;
def _w_ = anchors[1][9] enddef; def _s_ = anchors[1][13] enddef;

snail_t foo;
snail(foo, "demo");
showsnails foo;
forsuffixes it = _e_, _w_, _n_, _s_:
  draw foo.it withpen pencircle scaled 4pt withcolor darkred;
endfor;
\stopexample
\example[option=MP][own-anchor]{Your own names for anchor points}{\externalfigure[figures/a.pdf]}

\section{Regular Paths}

Except for that kind of direct paths produced by the \type{=>} macro as shown in Example \in[first-diagram], the snail module provides a set of macros that construct paths composed solely of horizontal and vertical segments; such paths are called regular paths. The macros are explained below; for a clearer grasp of how paths are constructed, we illustrate them with MetaPost's \type{pair} objects rather than the snail nodes. Keep in mind, however, that these macros can be applied directly to snail nodes like the \type{=>} macro.

The simplest regular paths are built with the \type{xto} or \type{yto} macros. Example \in[xto-and-yto] creates a horizontal path \type{p} and a vertical path \type{q} using \type{xto} and \type{yto} repectively. The syntax \boxquote{\type{a xto b}} is equivalent to \boxquote{\type{a -- (xpart b, ypart a)}}, and \boxquote{\type{a yto b}} is equivalent to \boxquote{\type{a -- (xpart a, ypart b)}}.

Note that \type{xto} and \type{yto} can be applied directly to nodes; the resulting paths are called flows. A flow can be displayed with the \type{showflow} macro. Example \in[first-diagram] already demonstrates how to create and display flows.

\startexample
pair a, b, c;
a := (1cm, 2cm); b := (4cm, 3cm); c := (5cm, 0); 

path p[];
p1 := a xto b; p2 := b yto c; p3 := c xto a;
drawarrow p1; drawarrow p2; drawarrow p3;
\stopexample
\example[option=MP][xto-and-yto]{Simplest regular paths}{\externalfigure[figures/08.pdf]}

The \type{xyto} and \type{yxto} macros create regular paths with a single turn. The \type{xyto} macro indicates the single turn is from horizontal to vertical direction; \type{yxto} does the reverse. Their usage is shown in Example \in[xyto-and-yxto]. These two macros can be combined to build more complex paths; see Example \in[complex-paths].

\startexample
p1 := a yxto b;
p2 := b xyto c;
/BTEX\blank[3*line]/ETEX
/BTEX\strut/ETEX
\stopexample
\example[option=MP][xyto-and-yxto]{Regular paths with single turn}{\externalfigure[figures/09.pdf]}

\startexample
p1 := a yxto b xyto c;
/BTEX\blank[3*line]/ETEX
/BTEX\strut/ETEX
\stopexample
\example[option=MP][complex-paths]{Regular paths with single turn}{\externalfigure[figures/10.pdf]}

If you want a path with two single turns, you can use the \type{xyxto} and \type{yxyto} macros. \type{xyx} indicates turns from horizontal to vertical and back to horizontal. \type{yxy} indicates turns from vertical to horizontal and back to vertical direction.

\startexample
pair a, b, c, d;
a := (0, 0);     b := (4cm, 2cm);
c := (4cm, 0);   d := (7cm, 2cm);
path p, q;
p := a xyxto b;  q := c yxyto d;
\stopexample
\example[option=MP][two-turns]{Regular paths with two turns}{\externalfigure[figures/11.pdf]}

The regular paths produced by the \type{xyto}, \type{yxto}, \type{xyxto} and \type{yxyto} macros can be smoothed with the \type{road} macro; all turns in paths are rendered as fillets. The \type{road} macro takes two arguments: the first is a path variable, and the second is a regular path. After smoothing, the resulting path is assigned to the first argument.

\startexample
pair a, b;
a := (0, 0); b := (4cm, 3cm);
path p;
road(p, a xyxto b);
\stopexample
\example[option=MP][road]{Smoothed paths}{\externalfigure[figures/12.pdf]}

To draw some unconventional diagrams, we need to define a macro that converts any regular or closed path into a smooth curve, so we have the macros \type{tocurve} and \type{toclosedcurve}. The example below creates some curve path with \type{tocurve} that only applies to open paths.

\startMP
snailfam_t a;
snail(a1, "$a_1$");
snail(a2, "$a_2$") at (4cm, 4cm);
showsnails a1, a2;

path e, f, p, q; string s;
p := a1 xyto a2; q := a1 yxto a2;
\stopMP
\startexample
pickup pencircle scaled 1pt;
for t = .25, .5, .75:
  s := "{\tfxx $t=" & decimal t & "$}";
  e := tocurve(p, t); f := tocurve(q, t);
  drawarrow e withcolor transparent(1, .5, t * red);
  annotate(e, s);
  drawarrow f withcolor transparent(1, .5, t * blue);
  annotate(f, s);
endfor;
\stopexample
\example[option=MP][]{Curve paths}{\externalfigure[figures/22.pdf]}

The second argument of \type{tocurve} is used to set the curve’s tension. Its usual range is [0, 1], but in fact there is no restriction—you may supply any value.

The \type{toclosedcurve} can translate a closed path to a closed curve, see the following example. Its second argument has the same meaning as in \type{tocurve}.

\startexample
path p, q;
p := fulldiamond xyscaled (6cm, 3cm);
q := toclosedcurve(p, .5);

drawpath p; drawpoints p;
draw q withpen pencircle scaled 2pt
       withcolor darkred;
\stopexample
\example[option=MP][closed-curve]{Closed curve}{\externalfigure[c.pdf]}

Regular paths and the smoothed paths produced by \type{road}, are all ordinary path objects. Because they carry no special metadata, they can only be drawn one by one with MetaPost's \type{drawarrow} macro. Consequently, when paths intersect, it is difficult to add crossing indicators.

The snail module provides the macro \type{showroads}, which draws all paths as arguments in a single pass. Internally, it detects any intersections and renders them with short gaps in the lines to indicate that one path passes over another. Need to note that \type{showroads} can't process two paths crossing multiple times at present.

\startexample
pair a, b, c, d;
a := (0,   0);  b := (4cm, 4cm);
c := (0, 4cm);  d := (4cm,   0);
path p, q;
road(p, a yxyto b); road(q, c xyxto d);
showroads p, q;
\stopexample
\example[option=MP][crossing]{Crossing paths}{\externalfigure[figures/13.pdf]}

\section{Annotation}

The snail module provides the \type{annotate} macro for annotating paths. This macro places some text based on the middle point of a path. Example \in[annotation] gives the usage of \type{annotate} and its side effect.

\startexample
path p[];
road(p1, (0, 0) xyxto (4cm, 3cm));
road(p2, (5cm, 0) yxto (8cm, 3cm));
showroads p1, p2;
annotate(p1, "road $p_1$");
annotate(p2, "road $p_2$");
\stopexample
\example[option=MP][annotation]{Path annotations}{\externalfigure[figures/14.pdf]}

The annotation of \type{p2} appears distorted because its center is aligned with the bend in \type{p2}. If you want to correct it, you need to use the \type{snap} macro which can take a part of a path as the annotating target instead of the origin path.

In MetaPost language, a path is parametrized over the interval $[0, 1]$, so every point ont it is obtained by evaluating the path at some \type{t} in $[0, 1]$. The \type{snap} marcro exploits this to extract a desired section from a path. Example \in[path-section] shows the part \type{[0.2, 0.6]} of the path \type{p}. By taking a section of the path as the annotating target, we can place a annotation at any position along this path.

\startexample
path p, q;
road(p, (0, 0) xyxto (4cm, 2cm));
q := snap(p, .2, .6);

pickup pencircle scaled 1pt;
draw p withcolor darkred;
draw q withpen pencircle scaled 6pt
       withcolor transparent(1, .3, darkblue);
\stopexample
\example[option=MP][path-section]{Path section}{\externalfigure[figures/15.pdf]}

Example \in[section-label] demonstrates how to place a annotation at a section of a path.

\startexample
path p;
road(p, (0, 0) yxto (3cm, 1.5cm));
showroads p;
annotate(snap(p, .3, 1), "road $p$");
\stopexample
\example[option=MP][section-label]{Placing annotation on section}{\externalfigure[figures/16.pdf]}

Sometimes, you need the annotation text to stand vertically upright instead of conforming to the deformation of the path. You can use the \type{tag} fufill this requirement and its usage is same with \type{annotate}; see Example \in[annotation-tail-trans].

\section{Irregular Nodes}

You can cretate your own shapes to serve as the node's frame by setting the module parameter \type{frame.shape}. For example, if you need a ellipse node, you only set \type{frame.shape} to \type{fullcircle}. If you want a circle node, you need to set \type{frame.isotropic} to \type{true}. 

\startexample
snail_t foo, bar;

snailenv.set("frame.shape", "'fullcircle'");
snail(foo, "ellipse");

snailenv.set("frame.isotropic", true);
snail(bar, "circle") at (0, -2cm);
\stopexample
\example[option=MP][ellipse-node]{Ellipse node}{\externalfigure[figures/18.pdf]}

Example \in[more-strange] applies the same technique to generate two more irregular node types. The \type{fulldiamond} and \type{tensecircle} macros are defined in MetaFun. Note the path assigned to the module parameter \type{frame.shpae} must be enclosed in single quotation marks, so that the result is wrapped into a MetaPost's string object. 

\startexample
snailfam_t a;

snailenv.set("frame.shape", "'fulldiamond'");
snail(a1, "diamond");
    
snailenv.set("frame.shape", "'fullsquare slanted .2'");
snail(a2, "parallelgram") at (0, -2.5cm);
\stopexample
\example[option=MP][more-strange]{More irregular nodes}{\externalfigure[figures/19.pdf]}

In fact, node's frame can be any closed path, so that you can turn almost any such shape into a node' frame. After creating irregular nodes, you can revert to snail's default node style with the \type{snailshapereset} macro. The following code defines an irregular node whose frame is a starfish with two macros we defined.

\startMP
vardef make_starfish =
  save t; numeric t; t := 0.38197;
  ((0, 1) -- t * (cosd 54, sind 54) -- (cosd 18, sind 18) 
   -- t * (cosd 18, -sind 18) -- (cosd 54, -sind 54) -- t * (0, -1)
   -- (-cosd 54, -sind 54) -- t * (-cosd 18, -sind 18) -- (-cosd 18, sind 18)
   -- t * (-cosd 54, sind 54) -- cycle) randomized .1
enddef;
\stopMP
\startMP
def starfish(suffix obj)(expr content) =
  snailenv.set("frame.shape", "'make_starfish'");
  snailenv.set("frame.isotropic", true);
  snail(obj, content);
  snailshapereset;
enddef;
\stopMP
\startMP
snail_t foo;
starfish(foo, "Starfish");

snailenv.set("debug", true);
showsnails foo;
\stopMP
\placeExample[here][starfish]{Starfish node}{\externalfigure[figures/24.pdf]}

\section{Module Parameters}

By now you've seen several examples where the \type{snailenv.set} macro is used to tweak the module parameters and thereby alter the diagram style. For example, to set the stroke thickness of a path to \type{1pt}, assign the parameter \type{path.thickness} as follows:

\startMP
snailenv.set("path.thickness", 1pt);
\stopMP

If you want to change the default color of the path to \type{darkred}, simply

\startMP
snailenv.set("path.color", darkred);
\stopMP

Note that the first argument to \type{snailenv.set} is always a string denoting the name of a module parameter, while the second argument may be any value or variable allowed in MetaPos except those of type \type{path} or \type{picture}.

Among the parameters, only \type{frame.shape} is of MetaPost's \type{path} type. To modify this parameter, refer to the previous examples: to pass such variables to the parameter table, you must enclosed them in single quotes then as MetaPost's string objects.

Some frequently-used parameters of the snail module are listed in the table below. This table is a Lua table because, within the \ConTeXt\ environment, MetaPost and Lua have become practically inseparable. While drawing, experiment freely—use \type{snailenv.set} to push the parameters in the table to exaggerated extremes and watch what each one actually does.

\starttyping
snailenv = {
  text = {
      fontsize = '/BTEX\color[darkred]{BodyFontSize}/ETEX',
      color = '/BTEX\color[darkred]{black}/ETEX'
  },
  frame = {
     shape = '/BTEX\color[darkred]{fullsquare}/ETEX',
     background = {color = '/BTEX\color[darkred]{.9white}/ETEX'},
     offset = '/BTEX\color[darkred]{BodyFontSize}/ETEX',
     thickness = '/BTEX\color[darkred]{.175BodyFontSize}/ETEX',
     color = '/BTEX\color[darkred]{.75white}/ETEX',
     isotropic = '/BTEX\color[darkred]{false}/ETEX',
     margin = '/BTEX\color[darkred]{3BodyFontSize}/ETEX',
  },
  path = {
     thickness = '/BTEX\color[darkred]{.125BodyFontSize}/ETEX',
     color = '/BTEX\color[darkred]{darkgray}/ETEX',
     directed = '/BTEX\color[darkred]{true}/ETEX',
     smooth = '/BTEX\color[darkred]{true}/ETEX',
     offset = '/BTEX\color[darkred]{.25BodyFontSize}/ETEX',
     fillet = '/BTEX\color[darkred]{.3BodyFontSize}/ETEX',
     ahvariant = 1, ahdimple = 1, ahlength = '/BTEX\color[darkred]{.5BodyFontSize}/ETEX',
  },
  debug = '/BTEX\color[darkred]{false}/ETEX'
}
\stoptyping

\section{Tail Transforms}

You may append not only the \type{at} macro call, but also any MetaPost's transformS, i.e. chains of shifted, rotated, etc. These transforms let you set a node’s center precisely while the node is being created.

The example below creates three nodes \type{a1}, \type{a2} and \type{a3}. Note how the tail transforms differ between \type{a2} and \type{a3}. The \boxquote{\type{at ...}} statement is equivalent to \boxquote{\type{shifted (...)}}, not  \boxquote{\type{shifted ...}}. Node \type{a2} is placed at the position obtained by taking \type{(0, -2cm)} and rotating it 60 degrees clockwise about the origin, but node \type{a3} is first placed at \type{(0, -2cm)} and then rotated 60 degrees counterclockwise about origin.

\startexample
snailfam_t a;
snail(a1, "$a_1$") at (0, 2cm);
snail(a2, "$a_2$") at (0, -2cm) rotated -60;
snail(a3, "$a_3$") shifted (0, -2cm) rotated 60;
showsnails a1, a2, a3;
/BTEX\blank[line]/ETEX
/BTEX\strut/ETEX
\stopexample
\example[option=MP][node-tail-trans]{Node tail transforms}{\externalfigure[figures/20.pdf]}

Likewise, when using the \type{annotate} macro adds labels to a path, additional transforms can also be supplied to fine-tune the placement of the label text. The example below continues from Example \in[node-tail-trans]. The annotation for path \type{p1} is rotated from the right side of the path to the left and then shifted leftward and downward.

\startexample
path p[];
p1 := a1 xyto a2; p2 := a2 xto a3.frame;
showflows p1, p2;
annotate(p1, "$p_1$")
    rotatedaround(point .5 along p1, 90)
    shifted (-7pt, -7.5mm);
tag(p1, "\inframed{$p_1$}") shifted (4pt, -4.5mm);
annotate(p2, "$p_2$");
\stopexample
\example[option=MP][annotation-tail-trans]{Annotation tail transforms}{\externalfigure[figures/21.pdf]}

\section{Empty node}

When use the \type{slug} macro to create a frameless node, you can set its text to an empty string then you get an empty node; for example:

\startMP
snail_t foo;
slug(foo, "") at somewhere;
\stopMP

In fact, an empty node is an invisible square which size is \type{BodyFontSize}---the font size of text. Bug you can display its all anchor points.

When I added the empty-node feature, I hadn't yet figured out how it would actually be used. I simply followed the intuition that \quotation{where there is substance, there must also be vold,} and gave the \type{slug} macro this capability. The only useage I can think of for it right now is to serve as a spacer between two nodes, so the layout no longer needs an explicit dimension. For example,

\startMP
snail_t empty, foo, bar;
slug(empty, "");
snail(foo, "i am foo");

put empty at foo.anchors[2][2] from empty.anchors[2][6];
snail(bar, "i am bar") at empty.anchors[2][2] from bar.anchors[2][6];

snailenv.set("debug", true);
showsnails empty, foo, bar;
\stopMP
\placeExample[force][empty-node]{Empty node usage}{\externalfigure[b.pdf]}

\section{A Few Examples}

The following is an example of a commutative diagram, in which the \type{startsnailway} and \type{stopsnailway} macros create a temporary environment for drawing paths with MetaPost's \type{draw} and \type{drawarrow} primtive macros.

\startMP
snailfam_t a;
slug(a1, "$X\times_Z Y$") at (-2cm, 2cm);
slug(a2, "$X$") at (2cm, 2cm);
slug(a3, "$Z$") at (2cm, -2cm);
slug(a4, "$Y$") at (-2cm, -2cm);
slug(a5, "$T$") at (-6cm, 6cm);
slug(a6, "$(x, y)$") at (-4cm, 4cm);
showsnails a1, a2, a3, a4, a5, a6;
\stopMP
\startMP
path p[];
p1 := a1 => a2; p2 := a2 => a3; p3 := a4 => a3; p4 := a1 => a4;
p5 := tocurve(a5 xyto a2, .75); p6 := tocurve(a5 yxto a4, .75);
p7 := (a5 => a6) && (a6 => a1); % note the usage of &&
showflows p1, p2, p3, p4, p5, p6;
startsnailway; drawarrow p7 withdashes 1mm; stopsnailway;
\stopMP
\startMP
tag(p1, "$p$"); tag(p2, "$f$"); tag(p3, "$g$");
tag(p4, "$q$"); tag(p5, "$x$"); tag(p6, "$y$");
\stopMP
\placeExample[here,force][]{Commutative diagram}{\externalfigure[figures/23.pdf]}

\subject{Afterword}

Everything the snail module can do has now been laid out. The tour was intentionally made of tiny vignettes; I never produced one genuinely useful flowchart—such a grand scene is meant for you.

If you actually plan to draw with snail, start on paper. A rough sketch is enough: mark the nodes and their approximate positions. From that sketch, create the nodes and place them, then switch the module’s debug parameter to true so every node and its anchors are drawn. Once the nodes and anchors are on the page, building and annotating the paths becomes nothing more than a game of Snake.

\stoptext
